﻿#region License
/*

	Copyright (C) 2008 Oliver Charles, Daniel Hollander

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
	
*/
#endregion

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Xml;

namespace Overload
{
    public sealed class SvgGrapher
    {
        private readonly Dictionary<string, PointF[]> datasets = new Dictionary<string, PointF[]>();
        private readonly Color[] dataSetColors = new Color[] { Color.SkyBlue, Color.Firebrick, Color.RosyBrown };

        public void AddDataSet(PointF[] points, string dataSetName)
        {
            datasets.Add(dataSetName, points);
        }

        public void Plot(string fileName, int width, int height)
        {
            SvgDocument document = new SvgDocument(fileName, width, height);

            // Find the maximum points
            PointF maximum = new PointF();
            foreach(PointF[] dataset in datasets.Values)
            {
                foreach(PointF point in dataset)
                {
                    maximum.X = Math.Max(maximum.X, point.X);
                    maximum.Y = Math.Max(maximum.Y, point.Y);
                }
            }

            // Decide on the scale factor
            PointF scaleFactor = new PointF(width / maximum.X, height / maximum.Y);

            document.AddLine(string.Format("M 0 0 L 0 {1} L {0} {1}", width, height), Color.DarkGray, 1);

            #region Plot Gridlines

            int verticalGridlineSpacing = 50;
            for (int i = 0; i < height; i += verticalGridlineSpacing)
            {
                int x = -60;
                int y = height - i;

                string linePathData = string.Format("M 0 {0} L {1} {0}", y, width);
                document.AddLine(linePathData, Color.Gray, 1);

                string text = string.Format("{0:#0.####}", (i / scaleFactor.Y) * 1000);
                document.AddText(x, y, text);
            }

            int horizontalGridlineSpacing = 100;
            for (int i = 0; i <= width; i += horizontalGridlineSpacing)
            {
                int x = i;
                int y = height + 20;

                string linePathData = string.Format("M {0} 0 L {0} {1}", i, height);
                document.AddLine(linePathData, Color.Gray, 1);

                string text = string.Format("{0}", i / scaleFactor.X);
                document.AddText(x, y, text);
            }

            #endregion

            #region Plot Data

            // Todo ermm, no thx
            int blah = 0;
            foreach(PointF[] dataset in datasets.Values)
            {
                Color dataSetColor = dataSetColors[blah++];

                string pathData = "";
                foreach(PointF dataPoint in dataset)
                {
                    string operation = "L";
                    if (dataset[0] == dataPoint)
                        operation = "M";

                    pathData += string.Format("{0} {1} {2:00.000} ",
                        operation, (dataPoint.X * scaleFactor.X), height - (dataPoint.Y * scaleFactor.Y));
                }

                document.AddLine(pathData, dataSetColor, 3);

                foreach (PointF dataPoint in dataset)
                    document.AddCircle(dataPoint.X*scaleFactor.X, height - dataPoint.Y*scaleFactor.Y, 5, 3, Color.White, dataSetColor);
            }

            #endregion

            document.Close();
        }

    }

    public class SvgDocument
    {
        private readonly XmlTextWriter writer;

        public SvgDocument(string fileName, int width, int height)
        {
            writer = new XmlTextWriter(fileName, Encoding.UTF8);

            writer.WriteStartDocument();
            writer.Indentation = 4;
            writer.IndentChar = ' ';
            writer.Formatting = Formatting.Indented;

            writer.WriteStartElement("svg");
            writer.WriteAttributeString("width", string.Format("{0:00}", width));
            writer.WriteAttributeString("height", string.Format("{0}", height));
        }

        public void Close()
        {
            writer.WriteEndElement();
            writer.WriteEndDocument();
            writer.Flush();
            writer.Close();
        }

        #region Plotting

        public void AddText(int x, int y, string text)
        {
            writer.WriteStartElement("text");
            writer.WriteAttributeString("x", x.ToString());
            writer.WriteAttributeString("y", y.ToString());
            writer.WriteString(text);
            writer.WriteEndElement();
        }

        public void AddCircle(float x, float y, float radius, float strokeWidth, Color fillColor, Color strokeColor)
        {
            writer.WriteStartElement("circle");
            writer.WriteAttributeString("cx", x.ToString());
            writer.WriteAttributeString("cy", y.ToString());
            writer.WriteAttributeString("r", radius.ToString());
            writer.WriteAttributeString("stroke", ColorTranslator.ToHtml(strokeColor));
            writer.WriteAttributeString("stroke-width", strokeWidth.ToString());
            writer.WriteAttributeString("fill", ColorTranslator.ToHtml(fillColor));
            writer.WriteEndElement();
        }

        public void AddLine(string lineData, Color pathColor, float lineWidth)
        {
            writer.WriteStartElement("path");
            writer.WriteAttributeString("d", lineData);
            writer.WriteAttributeString("stroke", ColorTranslator.ToHtml(pathColor));
            writer.WriteAttributeString("stroke-width", lineWidth.ToString());
            writer.WriteAttributeString("fill", "none");
            writer.WriteEndElement();
        }

        #endregion
    }
}